package jobManagement.impl;

/*
 This file is part of Open Grid Job Management System (OGJMS).

 OGJMS is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 OGJMS is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with OGJMS.  If not, see <http://www.gnu.org/licenses/>.

 The entire and newest source code can be found at http://code.google.com/p/ogjms/  

 */
import java.io.ByteArrayInputStream;

import org.apache.axis.components.uuid.UUIDGen;
import org.apache.axis.components.uuid.UUIDGenFactory;
import org.globus.axis.message.addressing.Address;
import org.globus.axis.message.addressing.EndpointReferenceType;
import org.globus.axis.message.addressing.ReferenceParametersType;
import org.globus.axis.util.Util;
import org.globus.exec.client.GramJob;
import org.globus.exec.client.GramJobListener;
import org.globus.exec.generated.StateEnumeration;
import org.globus.exec.utils.ManagedJobConstants;
import org.globus.gsi.GlobusCredential;
import org.globus.gsi.gssapi.GlobusGSSCredentialImpl;
import org.globus.wsrf.impl.SimpleResourceKey;
import org.globus.wsrf.impl.security.authentication.Constants;
import org.gridforum.jgss.ExtendedGSSCredential;
import org.gridforum.jgss.ExtendedGSSManager;
import org.ietf.jgss.GSSCredential;

import JobManagementUtils.JobManagementConstants;

public class JobManager extends Thread implements GramJobListener {

	public JobStatus jobStatus = new JobStatus();

	private int lifetime = 3600 * 12;

	// Credential objects
	private GlobusCredential globusCredential = null;
	private GlobusGSSCredentialImpl globusGSSCredential = null;
	private GSSCredential cred = null;

	private GramJob crtJob;
	private boolean jobCompleted;

	private int exitCode;
	
	private String proxy;
	private String jobDescription;
	int counter=0;

	static {
		Util.registerTransport();
	}
	
	public JobManager(String proxy, String jobDescription,int counter){
		this.proxy=proxy;
		this.jobDescription=jobDescription;
		this.counter=counter;
	}

	public void run() {
		try {
			this.globusCredential = new GlobusCredential(
					new ByteArrayInputStream(proxy.getBytes()));
			this.globusGSSCredential = new GlobusGSSCredentialImpl(
					globusCredential, lifetime);

			byte[] data = this.globusGSSCredential
					.export(ExtendedGSSCredential.IMPEXP_OPAQUE);

			ExtendedGSSManager manager = (ExtendedGSSManager) ExtendedGSSManager
					.getInstance();
			cred = manager.createCredential(data,
					ExtendedGSSCredential.IMPEXP_OPAQUE,
					GSSCredential.DEFAULT_LIFETIME, null,
					GSSCredential.INITIATE_AND_ACCEPT);
			System.out.println("[Credential] Credential username: "
					+ cred.getName());

			EndpointReferenceType factoryEPR = new EndpointReferenceType();
			factoryEPR.setAddress(new Address(
					JobManagementConstants.MANAGED_JOB_SERVICE_FACTORY));

			ReferenceParametersType props = new ReferenceParametersType();
			SimpleResourceKey key = new SimpleResourceKey(
					ManagedJobConstants.RESOURCE_KEY_QNAME, "Fork");
			props.add(key.toSOAPElement());

			factoryEPR.setParameters(props);

			int exitCode = -1;
			
			System.out.print("[Gram Job]Init:");
			this.crtJob = new GramJob(jobDescription);

			this.crtJob.setMessageProtectionType(Constants.ENCRYPTION);
			this.crtJob.addListener(this);
			this.crtJob.setCredentials(cred);

			System.out.println("\t OK");

			exitCode = processJob(factoryEPR, counter);
			System.out.println("[Job]Job exited with code: " + exitCode);

		} catch (Exception e) {
			System.out.println("\t Failed");
			e.printStackTrace();
		}

	}

	public boolean terminate() {
		boolean ok = true;
		try {

			ok = this.crtJob.terminate(true, false, true);
			if (ok) {
				this.jobStatus.setJobStatus("Stopped");
			}

		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return ok;
	}

	private int processJob(EndpointReferenceType factoryEPR, int counter) {

		UUIDGen jobuuid = UUIDGenFactory.getUUIDGen();
		String jobID = "uuid:" + jobuuid.nextUUID();

		boolean batchMode = false;
		boolean limitedDelegation = true;

		boolean submitted = false;
		int tries = 0;
		try {
			System.out.println("Submitting job with ID: " + jobID);
			while (false == submitted) {
				++tries;
				System.out.println("Try number: " + tries);
				this.crtJob.submit(factoryEPR, batchMode, limitedDelegation,
						jobID);
				submitted = true;
			}

			System.out.println("waiting for job to finish....");
			if (counter > 0) {
				this.waitJobCompletionTimed(counter);
			} else {
				this.waitJobCompletion();
			}
			System.out.println("[Job] done .Exit code: "
					+ this.crtJob.getExitCode());

			this.exitCode = this.crtJob.getExitCode();

		} catch (Exception ex) {
			System.out.println("Error submitting job:\n" + ex.getMessage());
			ex.printStackTrace();
		}

		return exitCode;
	}

	private synchronized void waitJobCompletion() {
		StateEnumeration oldState, crtState;
		try {
			oldState = this.crtJob.getState();
			while (false == this.jobCompleted) {
				try {
					this.wait(JobManagementConstants.STATE_CHANGE_BASE_TIMEOUT_MILLIS);
				} catch (InterruptedException ie) {
					System.out.println(ie.getMessage());
				}
				crtState = this.crtJob.getState();

				if (null != crtState && false == crtState.equals(oldState)) {
					oldState = crtState;
					System.out.println("Job state changed: "
							+ oldState.toString());
				}
			}
		} catch (Exception ex) {
			System.out.println("Error while waiting for job...\n"
					+ ex.getMessage());
			ex.printStackTrace();
		}
	}

	/**
	 * method used for timed waiting - will wait this.counter seconds and then
	 * assume job completed
	 */
	private synchronized void waitJobCompletionTimed(int counter) {
		StateEnumeration oldState, crtState;
		int cnt = 0;
		try {
			oldState = this.crtJob.getState();
			while ((false == this.jobCompleted) && (cnt < counter)) {
				++cnt;
				try {
					this
							.wait(JobManagementConstants.STATE_CHANGE_BASE_TIMEOUT_MILLIS);
				} catch (InterruptedException ie) {
					System.out.println(ie.getMessage());
				}
				crtState = this.crtJob.getState();
				System.out.println(cnt + " seconds");
				if (null != crtState && false == crtState.equals(oldState)) {
					oldState = crtState;
					// this.jobState = oldState.toString();
				}
			}
		} catch (Exception ex) {
			System.out.println("Error while waiting for job...\n"
					+ ex.getMessage());
			ex.printStackTrace();
		}

		if (counter == cnt) {
			System.out
					.println("Waited over 50 seconds for job completion... Will asume job DONE");
			this.jobCompleted = true;
		}
	}

	public void stateChanged(GramJob job) {
		StateEnumeration jobState = job.getState();

		synchronized (this) {
			this.jobStatus.setJobStatus(jobState.toString());
			System.out.println("[JobManager]New Status:" + this.jobStatus);
			if (true == jobState.equals(StateEnumeration.Done)
					|| true == jobState.equals(StateEnumeration.Failed)) {
				this.exitCode = job.getExitCode();
				this.jobCompleted = true;
			}

			this.notifyAll();
		}
	}

	public StateEnumeration getJobState() {
		return crtJob.getState();
	}

}
